import util from 'util'
import _ from 'lodash'

export class UsergridQuery {
  private query = ''
  private queryString!: string
  private _sort
  private _nextIsNot: boolean = false

  private useQuotesIfRequired(value) {
    return _.isFinite(value) ||
      // TODO: 添加isUuid判断
      // _.isUuid(value) ||
      _.isBoolean(value) ||
      (_.isObject(value) && !_.isFunction(value)) ||
      _.isArray(value)
      ? value
      : util.format(`'%s'`, value)
  }

  public toString(): string {
    if (this.queryString !== undefined) {
      return this.queryString
    } else {
      return util
        .format(
          'select * %s %s',
          this.query.length > 0 ? 'where ' + (this.query || '') : '',
          this._sort !== undefined ? this._sort : ''
        )
        .trim()
    }
  }

  public get and(): UsergridQuery {
    this.query = this.andJoin('')
    return this
  }

  public get or(): UsergridQuery {
    this.query = this.orJoin()
    return this
  }

  public get not(): UsergridQuery {
    this._nextIsNot = true
    return this
  }

  public eq(key, value): UsergridQuery {
    this.query = this.andJoin(util.format('%s = %s', key, this.useQuotesIfRequired(value)))
    return this
  }

  public gt(key, value) {
    this.query = this.andJoin(util.format('%s > %s', key, this.useQuotesIfRequired(value)))
    return this
  }

  public gte(key, value) {
    this.query = this.andJoin(util.format('%s >= %s', key, this.useQuotesIfRequired(value)))
    return this
  }

  public lt(key, value) {
    this.query = this.andJoin(util.format('%s < %s', key, this.useQuotesIfRequired(value)))
    return this
  }

  public lte(key, value) {
    this.query = this.andJoin(util.format('%s <= %s', key, this.useQuotesIfRequired(value)))
    return this
  }
  public contains(key, value) {
    this.query = this.andJoin(util.format('%s contains %s', key, this.useQuotesIfRequired(value)))
    return this
  }

  public locationWithin(distanceInMeters, lat, lng) {
    this.query = this.andJoin(util.format('location within %s of %s, %s', distanceInMeters, lat, lng))
    return this
  }
  public asc(key) {
    this.sort(key, 'asc')
    return this
  }
  public desc(key) {
    this.sort(key, 'desc')
    return this
  }

  public sort(key, order) {
    this._sort = key && order ? util.format(' order by %s %s', key, order) : ''
    return this
  }

  public fromString(queryString) {
    this.queryString = queryString
    return this
  }

  public andJoin(append) {
    if (this._nextIsNot) {
      append = util.format('not %s', append)
      this._nextIsNot = false
    }
    if (!append) {
      return this.query
    } else if (this.query.length === 0) {
      return append
    } else {
      return _.endsWith(this.query, 'and') || _.endsWith(this.query, 'or')
        ? util.format('%s %s', this.query, append)
        : util.format('%s and %s', this.query, append)
    }
  }

  public orJoin() {
    return this.query.length > 0 && !_.endsWith(this.query, 'or') ? util.format('%s or', this.query) : this.query
  }
}
